package com.teemor.common.datasource.remote.server;


import com.alibaba.fastjson.JSON;
import com.teemor.common.bean.utils.BeanConvertor;
import com.teemor.common.bean.model.EsOutputModel;
import com.teemor.common.bean.model.EsQueryModel;
import com.teemor.common.datasource.remote.IEsRemoteInterface;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.action.search.ClearScrollRequest;
import org.elasticsearch.action.search.ClearScrollResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchScrollRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MultiMatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;


import org.elasticsearch.index.reindex.DeleteByQueryRequest;
import org.elasticsearch.search.Scroll;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

/**
 * @Description:
 * @author zhoulk
 * @date: 2019/12/2
 */
@Service
public class EsRemoteService implements IEsRemoteInterface {

    private static final Logger LOGGER = LoggerFactory.getLogger(IEsRemoteInterface.class);

    @Autowired
    private RestHighLevelClient highLevelClient;

    public void flush(String index) {
        DeleteByQueryRequest deleteByQueryRequest = new DeleteByQueryRequest(index);

        deleteByQueryRequest.setQuery(new BoolQueryBuilder());
        try {
            highLevelClient.deleteByQuery(deleteByQueryRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    public <T> EsOutputModel<T> multiMatchQuery(EsQueryModel queryModel, Class<T> clazz) {
        String scrollId = queryModel.getScrollId();
        EsOutputModel<T> esOutputModel = new EsOutputModel<>();
        if (StringUtils.isNotBlank(scrollId)) {
            try {
                SearchHit[] searchHits = scroll(scrollId);
                List<T> resultList = new ArrayList<>();
                for (SearchHit searchHit : searchHits) {
                    T t = JSON.parseObject(searchHit.getSourceAsString(), clazz);
                    resultList.add(t);
                }
                esOutputModel.setData(resultList);
                return esOutputModel;
            } catch (IOException e) {
                LOGGER.error(e.getMessage(), e);
                return esOutputModel;
            } finally {
                esOutputModel.setScrollId(scrollId);
            }
        }
        final Scroll scroll = new Scroll(TimeValue.timeValueMinutes(10L));
        SearchRequest searchRequest = new SearchRequest("article");
        searchRequest.scroll(scroll);

        //设置搜索条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder filters = new BoolQueryBuilder();
        searchSourceBuilder.postFilter(filters);//添加filter
        searchSourceBuilder.size(queryModel.getSize());
        searchSourceBuilder.sort("createTime", SortOrder.DESC);
        searchRequest.source(searchSourceBuilder);

        String[] queryFields = queryModel.getQueryFields();
        String queryValue = queryModel.getQueryValue();
        if (queryFields != null && queryFields.length >0 && StringUtils.isNotBlank(queryValue)) {
            QueryBuilder multiMatchQuery = new MultiMatchQueryBuilder(queryValue, queryFields);
            searchSourceBuilder.query(multiMatchQuery);//添加搜索内容
        }

        Object filter = queryModel.getFilters();
        if (filter != null) {
            Class filterClazz = filter.getClass();
            Field[] fields = filterClazz.getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);
                try {
                    Object value = field.get(filter);
                    if (value != null) {
                        String fieldName = field.getName();
                        filters.must(QueryBuilders.matchQuery(fieldName, value));
                    }
                } catch (IllegalAccessException e) {
                    LOGGER.error(e.getLocalizedMessage(), e);
                }
            }
        }
        try {
            SearchResponse searchResponse = highLevelClient.search(searchRequest, RequestOptions.DEFAULT); //通过发送初始搜索请求来初始化搜索上下文
            scrollId = searchResponse.getScrollId();
            SearchHit[] searchHits = searchResponse.getHits().getHits();
            List<T> resultList = new ArrayList<>();
            for (SearchHit searchHit : searchHits) {
                T t = JSON.parseObject(searchHit.getSourceAsString(), clazz);
                resultList.add(t);
            }
            esOutputModel.setScrollId(scrollId);
            esOutputModel.setData(resultList);
        } catch (Exception e) {
            LOGGER.error(e.getLocalizedMessage(), e);
        } finally {
//            closeScroll(scrollId);
        }
        return esOutputModel;
    }

    private SearchHit [] scroll(String scrollId) throws IOException {
        final Scroll scroll = new Scroll(TimeValue.timeValueMinutes(10L));
        SearchScrollRequest scrollRequest = new SearchScrollRequest(scrollId); //创建一个新的搜索滚动请求，保存最后返回的滚动标识符和滚动间隔
        scrollRequest.scroll(scroll);
        SearchResponse searchResponse  = highLevelClient.scroll(scrollRequest, RequestOptions.DEFAULT);
        scrollId = searchResponse.getScrollId();
        SearchHit [] searchHits = searchResponse.getHits().getHits();
        return searchHits;
    }

    private void closeScroll(String scrollId) {
        if (StringUtils.isNotBlank(scrollId)) {
            boolean succeeded = false;
            do {
                ClearScrollRequest clearScrollRequest = new ClearScrollRequest(); //完成滚动后，清除滚动上下文
                clearScrollRequest.addScrollId(scrollId);
                ClearScrollResponse clearScrollResponse;
                try {
                    clearScrollResponse = highLevelClient.clearScroll(clearScrollRequest, RequestOptions.DEFAULT);
                    succeeded = clearScrollResponse.isSucceeded();
                } catch (IOException e) {
                    LOGGER.error(e.getMessage(), e);
                }
            } while (!succeeded);
        }
    }
}
